package com.lzyyj.gmis.base.dao.hibernate;

import com.lzyyj.gmis.base.dao.IntegerValuedEnum;
import com.lzyyj.gmis.base.dao.IntegerValuedEnumReflect;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.EnhancedUserType;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

public class IntegerValuedEnumType<T extends Enum<T> & IntegerValuedEnum> implements EnhancedUserType {

	/**
	 * Enum class for this particular custom type.
	 */
	private Class<T> enumClass;

	/**
	 * Value to use if null.
	 */
	private Integer defaultValue;

	public IntegerValuedEnumType() {
	}

	public void setParameterValues(Properties parameters) {
		String enumClassName = parameters.getProperty("enum");
		try {
			enumClass = (Class<T>) Class.forName(enumClassName).asSubclass(
					Enum.class).asSubclass(IntegerValuedEnum.class);
		} catch (ClassNotFoundException e) {
			throw new HibernateException("Enum class not found", e);
		}

		String defaultValueStr = parameters.getProperty("defaultValue");
		if (defaultValueStr != null && !defaultValueStr.isEmpty()) {
			try {
				setDefaultValue(Integer.parseInt(defaultValueStr));
			} catch (NumberFormatException e) {
				throw new HibernateException("Invalid default value", e);
			}
		}
	}

	public Integer getDefaultValue() {
		return defaultValue;
	}

	public void setDefaultValue(Integer defaultValue) {
		this.defaultValue = defaultValue;
	}

	/**
	 * The class returned by <tt>nullSafeGet()</tt>.
	 * 
	 * @return Class
	 */
	public Class returnedClass() {
		return enumClass;
	}

	@Override
	public int[] sqlTypes() {
		return new int[] { Types.TINYINT,Types.INTEGER};
	}

	@Override
	public boolean isMutable() {
		return false;
	}

	/**
	 * Retrieve an instance of the mapped class from a JDBC resultset.
	 * Implementors should handle possibility of null values.
	 * 
	 * @param resultSet
	 *            a JDBC result set
	 * @param names
	 *            the column names
	 * @param owner
	 *            the containing entity
	 * @return Object
	 * @throws HibernateException
	 * @throws SQLException
	 */
	public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
			throws SQLException {

		Integer value = resultSet.getInt(names[0]);
		if (value == null) {
			value = getDefaultValue();
			if (value == null) { // no default value
				return null;
			}
		}
		String name = IntegerValuedEnumReflect.getNameFromValue(enumClass,
				value);
		Object res = resultSet.wasNull() ? null : Enum.valueOf(enumClass, name);

		return res;
	}

	/**
	 * Write an instance of the mapped class to a prepared statement.
	 * Implementors should handle possibility of null values. A multi-column
	 * type should be written to parameters starting from <tt>index</tt>.
	 * 
	 * @param preparedStatement
	 *            a JDBC prepared statement
	 * @param value
	 *            the object to write
	 * @param index
	 *            statement parameter index
	 * @throws HibernateException
	 * @throws SQLException
	 */
	public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index)
			throws SQLException {

		if (value == null) {
			preparedStatement.setNull(index, Types.TINYINT);
		} else {
			preparedStatement.setInt(index, ((T) value).getValue());
		}
	}

	@Override
	public Object assemble(Serializable cached, Object owner) {
		return cached;
	}

	@Override
	public Serializable disassemble(Object value) {
		return (Enum) value;
	}

	@Override
	public Object deepCopy(Object value) {
		return value;
	}

	@Override
	public boolean equals(Object x, Object y) {
		return x == y;
	}

	@Override
	public int hashCode(Object x) {
		return x.hashCode();
	}

	@Override
	public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException, SQLException {
		Integer value = resultSet.getInt(names[0]);
		if (value == null) {
			value = getDefaultValue();
			if (value == null) { // no default value
				return null;
			}
		}
		String name = IntegerValuedEnumReflect.getNameFromValue(enumClass,
				value);
		Object res = resultSet.wasNull() ? null : Enum.valueOf(enumClass, name);

		return res;
	}

	@Override
	public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SharedSessionContractImplementor sharedSessionContractImplementor) throws HibernateException, SQLException {
		if (value == null) {
			preparedStatement.setNull(index, Types.TINYINT);
		} else {
			preparedStatement.setInt(index, ((T) value).getValue());
		}
	}

	@Override
	public Object replace(Object original, Object target, Object owner) {
		return original;
	}

	@Override
	public String objectToSQLString(Object value) {
		return '\'' + String.valueOf(((T) value).getValue()) + '\'';
	}

	@Override
	public String toXMLString(Object value) {
		return String.valueOf(((T) value).getValue());
	}

	@Override
	public Object fromXMLString(String xmlValue) {
		Integer value = Integer.parseInt(xmlValue);
		String name = IntegerValuedEnumReflect.getNameFromValue(enumClass,
				value);
		return Enum.valueOf(enumClass, name);
	}

	public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor arg2, Object arg3) throws HibernateException,
			SQLException {
		Integer value = resultSet.getInt(names[0]);
		if (value == null) {
			value = getDefaultValue();
			if (value == null) { // no default value
				return null;
			}
		}
		String name = IntegerValuedEnumReflect.getNameFromValue(enumClass,
				value);
		Object res = resultSet.wasNull() ? null : Enum.valueOf(enumClass, name);

		return res;
	}

	public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SessionImplementor arg3) throws HibernateException, SQLException {
		if (value == null) {
			preparedStatement.setNull(index, Types.TINYINT);
		} else {
			preparedStatement.setInt(index, ((T) value).getValue());
		}
	}
}
